home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Utilities / GOCR / src / pgm2asc.cc < prev    next >
C/C++ Source or Header  |  2000-05-29  |  64KB  |  1,642 lines

  1. #define Version "v0.2.4a4  2000/05/29" // update
  2. /*
  3. This is a Optical-Character-Recognition program
  4. Copyright (C) 2000  Joerg Schulenburg
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  19.  
  20.  Joerg.Schulenburg@physik.uni-magdeburg.de
  21.  
  22.   sometimes I have written comments in german language, sorry for that
  23.  
  24.  - look for ??? for preliminary code
  25.  - space: avX=22 11-13
  26.           avX=16  5-7
  27.           avX= 7  5-6
  28.          
  29.  ToDo: - add filter (r/s mismatch) g300c1
  30.        - write parallelizable code!
  31.        - learnmode (optimize filter)
  32.        - use ispell for final control or if unsure
  33.        - better line scanning (if not even)
  34.        - step 5: same chars differ? => expert mode
  35.   
  36.  
  37.    GLOBAL DATA (mostly structures)
  38.    - pix   : image - one byte per pixel  bits0-2=working
  39.    - lines : rows of the text (points to pix)
  40.    - box   : list of bounding box for character 
  41.    - obj   : objects (lines, splines, etc. building a character)
  42.  */
  43.  
  44.  
  45. #include <stdlib.h>
  46. #include <stdio.h>
  47. #include <assert.h>
  48. // #include <malloc.h>    // wegen Probleme mit delete ???
  49.  
  50. #include "pgm2asc.h"
  51. #include "pcx.h"
  52. /* ocr1 is the test-engine */
  53. #include "ocr1.h"
  54. /* first engine */
  55. #include "ocr0.h"
  56.  
  57. #define MaxBox (100*200)    // largest possible letter (buffersize)
  58. #define Uint  unsigned int
  59. #define Uchar unsigned char
  60. #define mmax(x,y)  (((x)>(y))?(x):(y))
  61.  
  62. // ostream& operator << ( ostream &os, int &i ){
  63. //  os << " " << setprecision(4) << i; return os; }
  64.  
  65. // ---------------------------------------------------------------------
  66. // list of letter boxes (root+end or end==root)
  67. // ---------------------------------------------------------------------
  68. struct box *box1=(struct box*)NULL,*box2,    // root, temp
  69.            *boxd=(struct box*)NULL;        // database
  70. struct environment env;
  71. // = {cs:160,avX:5,avY:8,p:NULL}; // some compilers do not like that
  72. struct tlines lines;
  73. int vvv=0;    // verbose
  74.  
  75. int n_run=0;  // num of run, if run_2 critical pattern get other results
  76.               // used for 2nd try, pixel uses slower filter function etc.
  77.  
  78. int in_str(char c, char *s){
  79.   int i;
  80.   for(i=0;s[i]!=0 && i<100;i++) if( s[i]==c ) return i+1;
  81.   return 0;
  82. }
  83.  
  84. // for sorting letters by position on the image
  85. // ToDo: - use function same line like this or include lines.m1 etc. 
  86. int box_gt(struct box *box1,struct box *box2){  // box1 after box2 ?
  87.       if(  box1->line > box2->line ) return 1;
  88.       if(  box1->line < box2->line ) return 0;
  89.       if(  box1->x0 > box2->x1  ) return 1; // before
  90.       if(  box1->x1 < box2->x0  ) return 0; // before
  91.       if(  box1->x0 > box2->x0  ) return 1; // before,  overlapping!
  92.       return 0;
  93. }
  94. // remove b2 from list, delete is done by the caller!
  95. void box_del(struct box *box2){
  96.   if(box2->pre ) box2->pre->next=box2->next;
  97.   if(box2->next) box2->next->pre=box2->pre;
  98.   if(box1==box2) box1=box2->next;
  99. }
  100. void box_ins_before(struct box *box2,struct box *box3){ // insert b3 before b2 in list
  101.   if(box1==NULL) { box1=box3;box3->next=box3->pre=(struct box*)NULL;return; }
  102.   box3->pre =box2->pre;
  103.   box2->pre =box3;
  104.   box3->next=box2;
  105.   if(box3->pre){
  106.     assert(box3->pre->next==box2);
  107.     box3->pre->next=box3;
  108.   }
  109.   if(box1==box2) box1=box3;
  110. }
  111.  
  112. void box_app(struct box **box1,struct box *box3){ // append b3 on list box1
  113.   assert(box3);            // must be a non NULL pointer from new()
  114.   if(*box1==NULL){ *box1=box3;box3->next=box3->pre=(struct box*)NULL;return; }
  115.   struct box *box2=*box1;while(box2->next) box2=box2->next; // search end
  116.   box3->pre =box2;
  117.   box2->next=box3;
  118.   box3->next=(struct box*)NULL;
  119. }
  120. // ------------------------ end list-funcs --------------------------
  121. // ------------------ (&~7)-pixmap-functions ------------------------
  122.  
  123. // test if pixel marked?
  124. int marked(pix &p, int  x, int  y){    // ret: 0=not_marked
  125.   if( x<0 || y<0 || x>=p.x || y>=p.y ) return 255 & 7;
  126.   return( p.p[x+y*p.x] & 7 ); }
  127.  
  128. #define Nfilt3 4 /* number of 3x3 filter */
  129. char filt3[Nfilt3][9]={ /* 2=ignore_pixel, 0=white_background, 1=black_pixel */
  130.   {0,0,0, 0,0,1, 1,0,0}, /* (-1,-1) (0,-1) (0,1)  (-1,0) (0,0) ... */
  131.   {0,0,0, 1,0,1, 0,0,0},
  132.   {1,0,0, 0,0,1, 0,0,0},
  133.   {1,1,0, 0,1,0, 2,1,1}
  134. };
  135. char c33[9];
  136.  
  137. // test if pixel --- later with error-correction
  138. int pixel(pix &p, int x, int y){    // ret: pixel-color (without marks)
  139.   if( x<0 || y<0 || x>=p.x || y>=p.y ) return 255 & ~7;
  140. #if 0
  141.   if((n_run&2) && x>0 && y>0 && x+1<p.x && y+1<p.y){ int r; // filter
  142.     r=(12*p.p[x+y*p.x]+p.p[x+1  +y*p.x]+p.p[x-1  +y*p.x]
  143.                       +p.p[x+(y+1)*p.x]+p.p[x+(y-1)*p.x])/16;
  144.     return r & ~7;
  145.   }
  146. #else
  147.   int i;
  148.   if(n_run&2){ // filter
  149.     c33[0]=c33[1]=c33[2]=c33[3]=c33[4]=c33[5]=c33[6]=c33[7]=c33[8]=0;
  150.     if(x>0     && y>0     ) c33[0]=p.p[x-1+(y-1)*p.x]>>7;
  151.     if(           y>0     ) c33[1]=p.p[x  +(y-1)*p.x]>>7;
  152.     if(x+1<p.x && y>0     ) c33[2]=p.p[x+1+(y-1)*p.x]>>7;
  153.     if(x>0                ) c33[3]=p.p[x-1+(y  )*p.x]>>7;
  154.                             c33[4]=p.p[x  +(y  )*p.x]>>7;
  155.     if(x+1<p.x            ) c33[5]=p.p[x+1+(y  )*p.x]>>7;
  156.     if(x>0     && y+1<p.y ) c33[6]=p.p[x-1+(y+1)*p.x]>>7;
  157.     if(           y+1<p.y ) c33[7]=p.p[x  +(y+1)*p.x]>>7;
  158.     if(x+1<p.x && y+1<p.y ) c33[8]=p.p[x+1+(y+1)*p.x]>>7;
  159.     for(i=0;i<Nfilt3;i++)
  160.     if( ( (filt3[i][0]>>1) || c33[0]!=(1 & filt3[i][0]) )
  161.      && ( (filt3[i][1]>>1) || c33[1]!=(1 & filt3[i][1]) )
  162.      && ( (filt3[i][2]>>1) || c33[2]!=(1 & filt3[i][2]) ) 
  163.      && ( (filt3[i][3]>>1) || c33[3]!=(1 & filt3[i][3]) )
  164.      && ( (filt3[i][4]>>1) || c33[4]!=(1 & filt3[i][4]) )
  165.      && ( (filt3[i][5]>>1) || c33[5]!=(1 & filt3[i][5]) ) 
  166.      && ( (filt3[i][6]>>1) || c33[6]!=(1 & filt3[i][6]) )
  167.      && ( (filt3[i][7]>>1) || c33[7]!=(1 & filt3[i][7]) )
  168.      && ( (filt3[i][8]>>1) || c33[8]!=(1 & filt3[i][8]) ) )
  169.     return ((filt3[i][4])?env.cs:0);
  170.     return p.p[x  +(y  )*p.x] & ~7;
  171.   }
  172. #endif
  173.   if((n_run&1) && x>0 && y>0 && x+1<p.x && y+1<p.y){ int r; // filter
  174.     r=p.p[x+y*p.x]&~7;
  175.     /* {2,2,2, 2,0,1, 2,1,0} */
  176.     if((r&128) && (~p.p[x+1  +y  *p.x]&128)
  177.                && (~p.p[x  +(y+1)*p.x]&128)
  178.                && ( p.p[x+1+(y+1)*p.x]&128)) r=64; // faxfilter
  179.     else
  180.     /* {2,2,2, 1,0,2, 0,1,2} */
  181.     if((r&128) && (~p.p[x-1  +y  *p.x]&128)
  182.                && (~p.p[x  +(y+1)*p.x]&128)
  183.                && ( p.p[x-1+(y+1)*p.x]&128)) r=64; // faxfilter
  184.     return r & ~7;
  185.   }
  186.   return( p.p[x+y*p.x] & ~7 ); }
  187.  
  188. /* modify pixel, test for out of range */
  189. void put(pix &p,int x,int y,int ia,int io){
  190.  if(x<p.x && x>=0 && y>=0 && y<p.y)
  191.  p.p[x+y*p.x]=(p.p[x+y*p.x] & ia) | io;
  192.  return;}
  193.  
  194. // modify n_run and print out what would happen on 2nd, 3th loop! 
  195. void out_b(pix b, int x0, int y0, int dx, int dy, int cs ){
  196.   int x,y,x2,y2,tx,ty,n1,n2;
  197.   static char *c1=".,,,,,,;@1234xoO";
  198.   tx=dx/80+1;ty=dy/40+1; // step, usually 1, but greater on large maps 
  199.   printf("# list pattern   x=%4d %4d d=%3d %3d t=%d %d\n",x0,y0,dx,dy,tx,ty);
  200.   for(y=y0;y<y0+dy;y+=ty) { // reduce the output to max 78x40
  201.     for(x=x0;x<x0+dx;x+=tx){ n1=n2=0;
  202.       for(y2=y;y2<y+ty && y2<y0+dy && n1==0;y2++) /* Mai2000 */
  203.       for(x2=x;x2<x+tx && x2<x0+dx && n1==0;x2++)
  204. #if 0
  205.       if((pixel(b,x2,y2)<cs)){ n1=8+marked(b,x2,y2); }
  206. #else
  207.       {
  208.         if((pixel(b,x2,y2)<cs)){ n1=8; }
  209.         if(n_run==0){
  210.           n_run++; if(!n1) if((pixel(b,x2,y2)<cs)){ n1= 9; }
  211.           n_run++; if(!n1) if((pixel(b,x2,y2)<cs)){ n1=10; }
  212.           n_run++; if(!n1) if((pixel(b,x2,y2)<cs)){ n1=11; }
  213.           n_run=0;
  214.         }
  215.       }
  216. #endif
  217.       printf("%c", c1[n1] );
  218.     }
  219.     if ( dx>0 ) printf("\n");
  220.   }
  221. }
  222.  
  223. void out_x(box *px){
  224.   printf("# list box dots=%d c=%c line=%d m= %d %d %d %d\n",
  225.         px->dots,px->c,px->line,
  226.         px->m1-px->y0,px->m2-px->y0,
  227.         px->m3-px->y0,px->m4-px->y0);
  228.   out_b(*(env.p),px->x0,px->y0,px->x1-px->x0+1,px->y1-px->y0+1,env.cs );
  229. }
  230.  
  231. void out_x2(box *box1,box *box2){
  232.   int x,y,i,tx,ty; static char *c1="OXXXXxx@",*c2=".,,,,,,,";
  233.   pix *b=env.p;
  234.   tx=(box1->x1-box1->x0)/40+1;
  235.   ty=(box1->y1-box1->y0)/40+1; // step, usually 1, but greater on large maps 
  236.   printf("\n# list 2 patterns");
  237.   for(i=0;i<=box1->y1-box1->y0;i+=ty) { // reduce the output to max 78x40???
  238.     printf("\n"); y=box1->y0+i;
  239.     for(x=box1->x0;x<=box1->x1;x+=tx) 
  240.     printf("%c", ((pixel(*b,x,y)<env.cs)?c1[marked(*b,x,y)]
  241.                                         :c2[marked(*b,x,y)]));
  242.     printf("  "); y=box2->y0+i; if(y<=box2->y1)
  243.     for(x=box2->x0;x<box2->x1;x+=tx)
  244.     printf("%c", ((pixel(*b,x,y)<env.cs)?c1[marked(*b,x,y)]
  245.                                         :c2[marked(*b,x,y)]));
  246.   }
  247. }
  248.  
  249. void out_b2(pix &b, int x0, int y0, int dx, int dy, int cs, int x1, int y1 ){
  250.   int x,y; static char *c1="OXXXXxx@",
  251.                        *c2=".,,,,,,,";
  252.   printf(" x=%5d %5d  d=%5d %5d\n",x0,y0,dx,dy);
  253.   for(y=y0;y<y0+dy;y++) {
  254.     for(x=x0;x<x0+dx;x++) 
  255.     printf("%c", ((pixel(b,x,y)<cs)?c1[marked(b,x,y)]
  256.                                    :c2[marked(b,x,y)]));
  257.     if( dx>0 ) printf(" = ");
  258.     for(x=x1;x<x1+dx;x++) 
  259.     printf("%c", ((pixel(b,x,y+y1-y0)<cs)?c1[marked(b,x,y+y1-y0)]
  260.                                          :c2[marked(b,x,y+y1-y0)]));
  261.     if ( dx>0 ) printf("\n");
  262.   }
  263. }
  264. // ------------------------ feature extraction -----------------
  265. // -------------------------------------------------------------
  266. // detect maximas in of line overlapps (return in %) and line koord
  267. #define HOR 1    // horizontal
  268. #define VER 2    // vertikal
  269. #define RIS 3    // rising=steigend
  270. #define FAL 4    // falling=fallend
  271. struct tline line;
  272.  
  273.  
  274. void swap(int *a,int *b){ int c=*a;*a=*b;*b=c; }
  275.  
  276. // kalkulate the overlapp of the line (0-1) with black points 
  277. // by rekursiv bisection 
  278. // gerade y=dy/dx*x+b, implizit d=F(x,y)=dy*x-dx*y+b*dx=0 
  279. // incrementell y(i+1)=m*(x(i)+1)+b, F(x+1,y+1)=f(F(x,y))
  280. // ret & 1 => inverse pixel!
  281. // d=2*F(x,y) ganze Zahlen
  282. int get_line(int x0, int y0, int x1, int y1, pix p, int cs, int ret){
  283.    int dx,dy,incrE,incrNE,d,x,y,r0,r1,ty,tx,
  284.        *px,*py,*pdx,*pdy,*ptx,*pty,*px1;
  285.    dx=abs(x1-x0); tx=((x1>x0)?1:-1);    // tx=x-spiegelung (new)  
  286.    dy=abs(y1-y0); ty=((y1>y0)?1:-1);    // ty=y-spiegelung (new)
  287.    // rotate coordinate system if dy>dx
  288.    if(dx>dy){ pdx=&dx;pdy=&dy;px=&x;py=&y;ptx=&tx;pty=&ty;px1=&x1; }
  289.    else     { pdx=&dy;pdy=&dx;px=&y;py=&x;ptx=&ty;pty=&tx;px1=&y1; }
  290.    if( *ptx<0 ){ swap(&x0,&x1);swap(&y0,&y1);tx=-tx;ty=-ty; }
  291.    d=(*pdy)*2-(*pdx); incrE=(*pdy)*2; incrNE=((*pdy)-(*pdx))*2;  
  292.    x=x0; y=y0; r0=r1=0; /* dd=tolerance (store max drift) */
  293.    while( (*px)<=(*px1) ){ 
  294.      if( ((pixel(p,x,y)<cs)?1:0)^(ret&1) ) r0++; else r1++;
  295.      (*px)++; if( d<=0 ){ d+=incrE; } else { d+=incrNE; (*py)+=(*pty); }
  296.    }
  297.    return (r0*(ret&~1))/(r0+r1); // ret==100 => percentage %
  298. }
  299.  
  300. // ret & 1 => inverse pixel!
  301. // d=2*F(x,y) integer numbers, ideal line: ,I pixel: I@
  302. //   ..@  @@@  .@.  ...,@2@. +1..+3 floodfill around line ???
  303. //   ..@  .@@  .@.  ...,.@@@ +2..+4 <= thats not implemented yet
  304. //   ..@  ..@  .@.  ...,.@@@ +2..+4
  305. //   @.@  @..  .@.  ...,@@@. +1..+3
  306. //   @.@  @@.  .@.  ...I@@@.  0..+3
  307. //   @@@  @@@  .@.  ..@1@@..  0..+2
  308. //   90%   0%  100%   90%     r1-r2
  309. int get_line2(int x0, int y0, int x1, int y1, pix p, int cs, int ret){
  310.    int dx,dy,incrE,incrNE,d,x,y,r0,r1,ty,tx,q,ddy,rx,ry,
  311.        *px,*py,*pdx,*pdy,*ptx,*pty,*px1;
  312.    dx=abs(x1-x0); tx=((x1>x0)?1:-1);    // tx=x-spiegelung (new)  
  313.    dy=abs(y1-y0); ty=((y1>y0)?1:-1);    // ty=y-spiegelung (new)
  314.    // rotate coordinate system if dy>dx
  315.    if(dx>dy){ pdx=&dx;pdy=&dy;px=&x;py=&y;ptx=&tx;pty=&ty;px1=&x1;rx=1;ry=0; }
  316.    else     { pdx=&dy;pdy=&dx;px=&y;py=&x;ptx=&ty;pty=&tx;px1=&y1;rx=0;ry=1; }
  317.    if( *ptx<0 ){ swap(&x0,&x1);swap(&y0,&y1);tx=-tx;ty=-ty; }
  318.    d=(*pdy)*2-(*pdx); incrE=(*pdy)*2; incrNE=((*pdy)-(*pdx))*2;  
  319.    x=x0; y=y0; r0=r1=0; ddy=3; // tolerance = bit 1 + bit 0 = left+right
  320.    while( (*px)<=(*px1) ){
  321.      q=((pixel(p,x,y)<cs)?1:0)^(ret&1);
  322.      if ( !q ){        // tolerance one pixel perpenticular to the line
  323.                         // what about 2 or more pixels tolerance???
  324.        ddy&=(~1)|(((pixel(p,x+ry,y+rx)<cs)?1:0)^(ret&1));
  325.        ddy&=(~2)|(((pixel(p,x-ry,y-rx)<cs)?1:0)^(ret&1))*2;
  326.      } else ddy=3;
  327.      if( ddy ) r0++; else r1++;
  328.      (*px)++; if( d<=0 ){ d+=incrE; } else { d+=incrNE; (*py)+=(*pty); }
  329.    }
  330.    return (r0*(ret&~1))/(r0+r1); // ret==100 => percentage %
  331. }
  332.  
  333. // look for white 2 or black 1 dots (3 = white+black)
  334. char get_bw(int x0, int x1, int y0, int y1,
  335.              pix p, int cs,int mask){
  336.   char rc=0;            // later with error < 2% (1 dot)
  337.   if(x0< 0  ) x0=0;  if(x1>=p.x) x1=p.x-1;
  338.   if(y0< 0  ) y0=0;  if(y1>=p.y) y1=p.y-1;
  339.   for(int y=y0;y<=y1;y++)
  340.   for(int x=x0;x<=x1;x++){
  341.     if( pixel(p,x,y)<cs) rc|=1; else rc|=2;    // break if rc==3
  342.     if( (rc & mask)==mask ) return mask;    // break loop
  343.   }
  344.   return(rc & mask);
  345. }
  346. // look for white 2 or black 1 dots (3 = white+black)
  347. char get_bw2(int x0, int x1, int y0, int y1,
  348.               pix p, int cs,int mask){
  349.   char rc=0;            // later with error < 2% (1 dot)
  350.   if(x0< 0  ) x0=0;  if(x1>=p.x) x1=p.x-1;
  351.   if(y0< 0  ) y0=0;  if(y1>=p.y) y1=p.y-1;
  352.   for(int y=y0;y<=y1;y++)
  353.   for(int x=x0;x<=x1;x++){
  354.     if( pixel(p,x,y+x*lines.dy/p.x)<cs) rc|=1; else rc|=2;    // break if rc==3
  355.     if( (rc & mask)==mask ) return mask;    // break loop
  356.   }
  357.   return(rc & mask);
  358. }
  359. // count black part of a line (modulo w)
  360. int get_line3(int x0, int x1, int y0, int y1, pix p,int cs,int w){
  361.   int rc=0,x,y,i,d,ww;        // rc=crossings  col=0=white
  362.   int dx=x1-x0, dy=y1-y0;
  363.   if( abs(dx)>=abs(dy) ) d=abs(dx); else d=abs(dy);
  364.   if(d)for(ww=i=0,x=x0,y=y0; i<=d; i++){
  365.     x=x0+i*dx/d; y=y0+i*dy/d;
  366.     if( ((pixel(p,x,y)<cs)?1:0) ) ww=w;    // 0=white 1=black
  367.     if (ww) { ww--;rc++; }
  368.   } 
  369.   return rc;
  370. }
  371. // more general Mar2000 (x0,x1,y0,y1 instead of x0,y0,x1,y1! (history))
  372. // look for black crossing a line x0,y0,x1,y1
  373. // follow line and count crossings ([white]-black-transitions)
  374. int num_cross(int x0, int x1, int y0, int y1, pix p,  int cs){
  375.   int rc=0,col=0,k,x,y,i,d;        // rc=crossings  col=0=white
  376.   int dx=x1-x0, dy=y1-y0;
  377.   if( abs(dx)>=abs(dy) ) d=abs(dx); else d=abs(dy);
  378.   for(i=0,x=x0,y=y0; i<=d; i++){
  379.     if(d){ x=x0+i*dx/d; y=y0+i*dy/d; }
  380.     k=((pixel(p,x,y)<cs)?1:0);    // 0=white 1=black
  381.     if(col==0 && k==1) rc++; col=k;
  382.   } 
  383.   return rc;
  384. }
  385. // -------------------------------------------------------------
  386. // mark edge-points
  387. //  - first move forward until b/w-edge
  388. //  - more than 2 pixel?
  389. //  - loop around
  390. //    - if forward    pixel : go up, rotate right
  391. //    - if forward no pixel : rotate left
  392. //  - stop if found first 2 pixel in same order
  393. // mit an rechter-Wand-entlang-gehen strategie
  394. // --------------------------------------------------------------
  395. // turmite game: inp: start-x,y, regel r_black=UP,r_white=RIght until border
  396. //               out: last-position
  397. // Zaehle dabei, Schritte,Sackgassen,xmax,ymax,ro-,ru-,lo-,lu-Ecken
  398. //
  399. // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  400. // GUTE IDEE VON MIR!!! KI-Methode!!! oder nicht???
  401. // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  402. //
  403. void turmite(pix &p, int &x, int &y, 
  404.              int x0, int x1, int y0, int y1, int cs, int rw, int rb){
  405.   int r;
  406.   if( x0<0  || y0<0  || x1>=p.x || y1>=p.y ) return;    // out of pixmap
  407.   for(;;){
  408.     if( x<x0 || y<y0 || x>x1  || y>y1  ) break;        // out of box
  409.     r = ( (pixel(p,x,y)<cs) ? rb : rw );        // select rule 
  410.     if( r==UP ) y--;   else
  411.     if( r==DO ) y++;   else
  412.     if( r==RI ) x++;   else
  413.     if( r==LE ) x--;   else
  414.     if( r==ST ) break; else assert(0);
  415.   }
  416. }
  417.  
  418. // search a way from p0 to p1 without crossing pixels of type t
  419. //  only two directions, usefull to test if there is a gap 's'
  420. // labyrint algo - do you know a faster way?
  421. int joined(pix &p, int x0, int y0, int x1, int y1, int cs){
  422.   int t,r,x,y,dx,dy,xa,ya,xb,yb;
  423.   x=x0;y=y0;dx=1;dy=0;
  424.   if(x1>x0){xa=x0;xb=x1;}else {xb=x0;xa=x1;}
  425.   if(y1>y0){ya=y0;yb=y1;}else {yb=y0;ya=y1;}
  426.   t=((pixel(p,x,y)<cs)?1:0);
  427.   for(;;){
  428.     if( t==((pixel(p,x+dy,y-dx)<cs)?1:0)    // right free?
  429.      && x+dy>=xa && x+dy<=xb && y-dx>=ya && y-dx<=yb) // wall
  430.          { r=dy;dy=-dx;dx=r;x+=dx;y+=dy; } // rotate right and step forward
  431.     else { r=dx;dx=-dy;dy=r; } // rotate left
  432.     // printf(" path xy %d-%d %d-%d %d %d  %d %d\n",xa,xb,ya,yb,x,y,dx,dy);
  433.     if( x==x1 && y==y1 ) return 1;
  434.     if( x==x0 && y==y0 && dx==1) return 0;
  435.   }
  436.   return 0; // endless loop ?
  437. }
  438.  
  439. // move from x,y to direction r until pixel==col or l steps
  440. // return number of steps
  441. int loop(pix &p,int x,int y,int l,int cs,int col,int r){ int i;
  442.   static const int rr[5][2]={{0,0},{0,-1},{0,1},{1,0},{-1,0}};
  443.   for(i=0;i<l && x>=0 && y>=0 && x<p.x && y<p.y;i++){
  444.     if( ((pixel(p,x,y)<cs)?1:0)^col ) break;
  445.     x+=rr[r][0]; y+=rr[r][1];
  446.   }
  447.   return i;
  448. }
  449. // mark recursiv if no-pixel-neighbours=0 connected with (x,y)
  450. // better with neighbours of same color (more general) ???
  451. // (&~7)-pixmap, start-point, critical_value, mark
  452. // mark neighbouring pixel of same color, return number
  453. int mark_nn(pix &p, int  x, int  y, int &cs, int r){
  454.   if( x<0 || y<0 || x>=p.x || y>=p.y) return 0;    // out of limits
  455.   if( marked(p,x,y) ) return 0;        // already marked
  456.   p.p[x+y*p.x] |= (r&7);        // mark (better direction)
  457.   int  rc=1;
  458.   int  i=((pixel(p,x  ,y  )<cs)?0:1);
  459.   if( i==((pixel(p,x+1,y  )<cs)?0:1) ) rc+=mark_nn(p,x+1,y  ,cs,r);
  460.   if( i==((pixel(p,x-1,y  )<cs)?0:1) ) rc+=mark_nn(p,x-1,y  ,cs,r);
  461.   if( i==((pixel(p,x  ,y+1)<cs)?0:1) ) rc+=mark_nn(p,x  ,y+1,cs,r);
  462.   if( i==((pixel(p,x  ,y-1)<cs)?0:1) ) rc+=mark_nn(p,x  ,y-1,cs,r);
  463.   if(n_run&1){ // second run (use diagonal points)
  464.     // diag-code added Mai99
  465.     // if( i==1 ) return rc; // only one color diagonal
  466.     if( i==((pixel(p,x+1,y+1)<cs)?0:1) ) rc+=mark_nn(p,x+1,y+1,cs,r);
  467.     if( i==((pixel(p,x-1,y-1)<cs)?0:1) ) rc+=mark_nn(p,x-1,y-1,cs,r);
  468.     if( i==((pixel(p,x-1,y+1)<cs)?0:1) ) rc+=mark_nn(p,x-1,y+1,cs,r);
  469.     if( i==((pixel(p,x+1,y-1)<cs)?0:1) ) rc+=mark_nn(p,x+1,y-1,cs,r);
  470.   }
  471.   return rc;
  472. }
  473. // use lowest three bits for mark
  474. void frame_nn(pix &p, int  x,  int  y,
  475.              int &x0, int &x1, int &y0, int &y1,    // enlargeframe
  476.              int cs, int r){
  477.   static int stackdepth=0,msg=0;
  478.   if( x<0 || y<0 || x>=p.x || y>=p.y) return;    // out of limits
  479.   if( marked(p,x,y) ) return;            // already marked
  480.   stackdepth++; // if(stackdepth==1)msg=0;
  481.   if(stackdepth==10000) {
  482.     if(!msg) fprintf(stderr,"\n ERROR: Stack overflow! Large objects!");
  483.     msg=1;stackdepth--;return;
  484.   }
  485.   p.p[x+y*p.x] |= (r&7);        // mark (better direction)
  486.   if(x<x0) x0=x;  if(x>x1) x1=x;        // enlarge frame
  487.   if(y<y0) y0=y;  if(y>y1) y1=y;
  488.   int  i=((pixel(p,x  ,y  )<cs)?0:1);
  489.   if( i==((pixel(p,x+1,y  )<cs)?0:1) ) frame_nn(p,x+1,y  ,x0,x1,y0,y1,cs,r);
  490.   if( i==((pixel(p,x-1,y  )<cs)?0:1) ) frame_nn(p,x-1,y  ,x0,x1,y0,y1,cs,r);
  491.   if( i==((pixel(p,x  ,y+1)<cs)?0:1) ) frame_nn(p,x  ,y+1,x0,x1,y0,y1,cs,r);
  492.   if( i==((pixel(p,x  ,y-1)<cs)?0:1) ) frame_nn(p,x  ,y-1,x0,x1,y0,y1,cs,r);
  493.   // diag-code added Mai99
  494.   if( i==((pixel(p,x+1,y+1)<cs)?0:1) ) frame_nn(p,x+1,y+1,x0,x1,y0,y1,cs,r);
  495.   if( i==((pixel(p,x-1,y-1)<cs)?0:1) ) frame_nn(p,x-1,y-1,x0,x1,y0,y1,cs,r);
  496.   if( i==((pixel(p,x-1,y+1)<cs)?0:1) ) frame_nn(p,x-1,y+1,x0,x1,y0,y1,cs,r);
  497.   if( i==((pixel(p,x+1,y-1)<cs)?0:1) ) frame_nn(p,x+1,y-1,x0,x1,y0,y1,cs,r);
  498.   stackdepth--; return;
  499. }
  500. // clear lowest 3 (marked) bits 
  501. void clr_bits(pix &p, int x0, int x1, int y0, int y1){ int x,y;
  502.   for(y=y0;y<=y1;y++)
  503.   for(x=x0;x<=x1;x++)  p.p[x+y*p.x] &= ~7;
  504. }
  505.  
  506. // --- copy part of pix p into new pix b    ---- len=10000
  507. int copybox( pix p, int x0, int y0, int dx, int dy, pix *b, int len){
  508.   int x,y;  b->x=b->y=0;
  509.   if(b->p==NULL || dx<0 || dy<0 || dx*dy>len){
  510.     printf(" error-copybox x=%5d %5d  d=%5d %5d\n",x0,y0,dx,dy);
  511.     return 1;
  512.   }
  513.   b->x = dx;   b->y = dy;   b->bpp = 1;
  514.   for(x=0;x<dx;x++)
  515.   for(y=0;y<dy;y++) b->p[x+y*dx] = pixel(p,x+x0,y+y0);
  516.   return 0;
  517. }
  518.  
  519. // look for white holes surrounded by black points
  520. // at moment white point with black in all for directions
  521. // later: count only holes with vol>10% ???
  522. int num_hole(int x0, int x1, int y0, int y1, pix p, int cs){
  523.   int rc=0,x,y;            // rc:1=hole
  524.   static Uchar buf[MaxBox];    // oder 2nd copy of picture, for working 
  525.   pix b;            // temporary mini-page
  526.   int dx=x1-x0+1, dy=y1-y0+1;
  527.   b.p = buf;
  528. //  b.p = new Uchar[ dy*dx ];     // buffer
  529. //  b.p = (Uchar *)malloc(dy*dx);
  530. //  assert(b.p);
  531.   if( copybox(p,x0,y0,dx,dy,&b,MaxBox) ) return -1;
  532.   // --- mark white-points connected with border        // if(opt&1)
  533.   for(x=0;x<b.x;x++) if(pixel(b,x,    0)>=cs) mark_nn(b,x,    0,cs,AT);        // erweiterung:
  534.   for(x=0;x<b.x;x++) if(pixel(b,x,b.y-1)>=cs) mark_nn(b,x,b.y-1,cs,AT);        //  nur optional        
  535.   for(y=0;y<b.y;y++) if(pixel(b,0    ,y)>=cs) mark_nn(b,0    ,y,cs,AT);        //  ist wie
  536.   for(y=0;y<b.y;y++) if(pixel(b,b.x-1,y)>=cs) mark_nn(b,b.x-1,y,cs,AT);        // zusaetzl.linie
  537.   // out_b(b,0,0,b.x,b.y,cs);
  538.   // --- look for unmarked white points => hole
  539.   for(x=0;x<b.x;x++)
  540.   for(y=0;y<b.y;y++) if( !marked(b,x,y)   )        // unmarked
  541.                      if( pixel(b,x,y)>=cs )        // hole found
  542.              { if( mark_nn(b,x,y,cs,AT)>1 || dx*dy<=40) rc++; }    
  543. //  delete[] b.p;        // segmentation fault
  544. //  free(b.p);
  545.   return rc;
  546. }
  547.  
  548. // count for black nonconnected objects --- used for i,auml,ouml,etc.
  549. int num_obj(int x0, int x1, int y0, int y1, pix  p, int cs){
  550.   int x,y,rc=0;            // rc=num_obj
  551.   static Uchar buf[MaxBox];    // oder 2nd copy of picture, for working
  552.   pix b;
  553.   b.p = buf;
  554.   if( copybox(p,x0,y0,x1-x0+1,y1-y0+1,&b,MaxBox) ) return -1;
  555.   // --- mark black-points connected with neighbours
  556.   for(x=0;x<b.x;x++) 
  557.   for(y=0;y<b.y;y++) if( pixel(b,x,y) < cs )
  558.                      if( !marked(b,x,y)    ){
  559.     rc++; mark_nn(b,x,y,cs,AT);
  560.   }
  561.   return rc;
  562. }
  563.  
  564. // ----------------------------------------------------------------------
  565. // filter-funktionen um bestimmte Buchstaben auszuschliessen
  566. // wenn Elemente aus A+B nicht ausgeschlossen 
  567. // entferne alle Elemente A (B) aus Gruppe zulaessiger
  568. char *list="0123456789,.\0xe4\0xf6\0xfc"    // "a=228 o=246 u=252
  569.            "abcdefghijklmnopqrstuvwxyz"
  570.            "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  571. int  wert[100];
  572. int  listlen=0,numrest=0;
  573. void ini_list(){ int i;
  574.     for(i=0;list[i]!=0 && i<100;i++) wert[i]=0;
  575.     numrest=listlen=i; } 
  576. void excude(char *filt){ int i,j;
  577.     for(j=0;filt[j]!=0 && j<100;j++)
  578.     for(i=0;list[i]!=0 && i<100;i++)
  579.     if( filt[j]==list[i] ) { if(!wert[i])numrest--; wert[i]++; } }
  580. char getresult(){ int i;
  581.     if( numrest==1 )
  582.     for(i=0;list[i]!=0 && i<100;i++) if(!wert[i]) return list[i];
  583.     return '_';
  584.  }
  585. //  look at the environment of the pixel too (contrast etc.)
  586. //   detailed analysis only of diff pixels!
  587. //
  588. // 100% * Abstand, 0 is best fit
  589. // = Aehnlichkeit 2er Buchstaben fuer Erkennung verstuemmelter Zeichen
  590. //   weigth of pixels with only one same neighbour set to 0
  591. //   look at contours too!
  592. int distance( pix p1, box *box1,
  593.               pix p2, box *box2, int cs){
  594.    int rc=0,x,y,v1,v2,i1,i2,rgood=0,rbad=0,x1,y1,x2,y2,dx,dy,dx1,dy1,dx2,dy2;
  595.    x1=box1->x0;y1=box1->y0;x2=box2->x0;y2=box2->y0;
  596.    dx1=box1->x1-box1->x0+1; dx2=box2->x1-box2->x0+1; dx=((dx1>dx2)?dx1:dx2);
  597.    dy1=box1->y1-box1->y0+1; dy2=box2->y1-box2->y0+1; dy=((dy1>dy2)?dy1:dy2);
  598.    if(abs(dx1-dx2)>1+dx/16 || abs(dy1-dy2)>1+dy/16) return 100;
  599.    // compare relations to baseline and upper line
  600.    if(2*box1->y1>box1->m3+box1->m4 && 2*box2->y1<box2->m3+box2->m4) rbad+=128;
  601.    if(2*box1->y0>box1->m1+box1->m2 && 2*box2->y0<box2->m1+box2->m2) rbad+=128;
  602.    // compare pixels
  603.    for( y=0;y<dy;y++ )
  604.    for( x=0;x<dx;x++ ) {    // try global shift too ???
  605.      v1     =((pixel(p1,x1+x  ,y1+y  )<cs)?1:0); i1=8;    // better gray?
  606.      v2     =((pixel(p2,x2+x  ,y2+y  )<cs)?1:0); i2=8;    // better gray?
  607.      if(v1==v2) { rgood+=16; continue; } // all things are right!
  608.      // what about different pixel???
  609.      // test overlapp of surounding pixels ???
  610.      v1=-1;
  611.      for(i1=-1;i1<2;i1++)
  612.      for(i2=-1;i2<2;i2++)if(i1!=0 || i2!=0){
  613.        if( ((pixel(p1,x1+x+i1*(1+dx/32),y1+y+i2*(1+dy/32))<cs)?1:0)
  614.          !=((pixel(p2,x2+x+i1*(1+dx/32),y2+y+i2*(1+dy/32))<cs)?1:0) ) v1++;
  615.      }
  616.      if(v1>0)rbad+=16*v1;
  617.    }
  618.    if(rgood+rbad) rc= 100*rbad/(rgood+rbad); else rc=99;
  619. //   if(rc<10 && vvv){
  620. //     printf(" distance rc=%d\n",rc);
  621. //     out_x(box1);out_x(box2);
  622. //   }
  623.    return rc;
  624. }
  625. // new variant
  626. //  look at the environment of the pixel too (contrast etc.)
  627. //   detailed analysis only of diff pixels!
  628. //
  629. // 100% * distance, 0 is best fit
  630. // = similarity of 2 chars for recognition of noisy chars
  631. //   weigth of pixels with only one same neighbour set to 0
  632. //   look at contours too!
  633. int distance2( pix p1, box *box1,
  634.                pix p2, box *box2, int cs){
  635.    int rc=0,x,y,v1,v2,i1,i2,rgood=0,rbad=0,
  636.        x1,y1,x2,y2,dx,dy,dx1,dy1,dx2,dy2,tx,ty;
  637.    x1=box1->x0;y1=box1->y0;x2=box2->x0;y2=box2->y0;
  638.    dx1=box1->x1-box1->x0+1; dx2=box2->x1-box2->x0+1; dx=((dx1>dx2)?dx1:dx2);dx=dx1;
  639.    dy1=box1->y1-box1->y0+1; dy2=box2->y1-box2->y0+1; dy=((dy1>dy2)?dy1:dy2);dy=dy1;
  640.    if(abs(dx1-dx2)>1+dx/16 || abs(dy1-dy2)>1+dy/16) rbad++; // how to weight?
  641.    // compare relations to baseline and upper line
  642.    if(box1->m4>0 && box2->m4>0){  // used ???
  643.      if(2*box1->y1>box1->m3+box1->m4 && 2*box2->y1<box2->m3+box2->m4) rbad+=128;
  644.      if(2*box1->y0>box1->m1+box1->m2 && 2*box2->y0<box2->m1+box2->m2) rbad+=128;
  645.    }
  646.    tx=dx/16; if(dx<17)tx=1; // raster
  647.    ty=dy/32; if(dy<33)ty=1;
  648.    // compare pixels
  649.    for( y=0;y<dy;y+=ty )
  650.    for( x=0;x<dx;x+=tx ) {    // try global shift too ???
  651.      v1=((pixel(p1,x1+x*dx1/dx,y1+y*dy1/dy)<cs)?1:0); i1=8;    // better gray?
  652.      v2=((pixel(p2,x2+x*dx2/dx,y2+y*dy2/dy)<cs)?1:0); i2=8;    // better gray?
  653.      if(v1==v2) { rgood+=16; continue; } // all things are right!
  654.      // what about different pixel???
  655.      // test overlapp of surounding pixels ???
  656.      v1=1;
  657.      v1=-1;
  658.      for(i1=-1;i1<2;i1++)
  659.      for(i2=-1;i2<2;i2++)if(i1!=0 || i2!=0){
  660.        if( ((pixel(p1,x1+x*dx1/dx+i1*(1+dx1/32),y1+y*dy1/dy+i2*(1+dy1/32))<cs)?1:0)
  661.          !=((pixel(p2,x2+x*dx2/dx+i1*(1+dx2/32),y2+y*dy2/dy+i2*(1+dy2/32))<cs)?1:0) ) v1++;
  662.      }
  663.      if(v1>0)
  664.      rbad+=16*v1;
  665.    }
  666.    if(rgood+rbad) rc= 100*rbad/(rgood+rbad); else rc=99;
  667.    if(/* rc<10 && */ vvv&1024){
  668. #define DEBUG 2
  669. #if DEBUG == 2
  670.      printf(" distance2 rc=%d rgood=%d rbad=%d\n",rc,rgood,rbad);
  671.      out_b(p1,box1->x0,box1->y0,box1->x1-box1->x0+1,
  672.                                 box1->y1-box1->y0+1,cs);
  673.      out_b(p2,box2->x0,box2->y0,box2->x1-box2->x0+1,
  674.                                 box2->y1-box2->y0+1,cs);
  675. //     out_x(box1);
  676. //     out_x(box2);
  677. #endif
  678.    }
  679.    return rc;
  680. }
  681.  
  682. // ============================= call OCR engine ================== ;)
  683. char whatletter(struct box *box1, int cs){
  684.    pix p=*(box1->p);
  685.    int    x,y,dots,xa,ya,x0,x1,y0,y1;
  686.    int  dx=x1-x0+1,dy=y1-y0+1;    // size
  687.    char    bc='_';                // best letter
  688.    char um=' ';                // umlaut? '"
  689.    xa=box1->x; ya=box1->y;
  690.    x0=box1->x0;y0=box1->y0;
  691.    x1=box1->x1;y1=box1->y1;
  692.    // int vol=(y1-y0+1)*(x1-x0+1);    // volume
  693.    // crossed l-m , divided chars
  694.    while( get_bw(x0,x1,y0,y0,p,cs,1)!=1  &&  y0+1<y1) y0++;
  695.    while( get_bw(x0,x1,y1,y1,p,cs,1)!=1  &&  y0+1<y1) y1--;
  696.    dx=x1-x0+1;
  697.    dy=y1-y0+1;    // size
  698.  
  699.    // better to proof the white frame too!!! ????
  700.    // --- test for german umlaut and points above, not robust enough???
  701.    // if three chars are connected i-dots (ari) sometimes were not detected
  702.    //  - therefore after division a test could be usefull
  703. #if 1
  704.    dots=box1->dots;
  705.    if( dots==0 )
  706.    if( dy>4  && 2*y0<box1->m1+box1->m2 )
  707.    {  // tall box ij"a"o"u
  708.       for(y=y0;2*y<y0+y1;y++) if( get_bw(x0,x1,y,y,p,cs,1)==0 ) break; // gap
  709.       if( 2*y<y0+y1 )
  710.       if( get_bw(x0,x0,y0,y,p,cs,1)==0 ) // be sure there are gap to neighbours
  711.       if( get_bw(x1,x1,y0,y,p,cs,1)==0 )
  712.       { 
  713.         box1->dots=dots=1;um='\''; 
  714.         if( dx>2 && num_obj(x0,x1,y0,y,p,cs)>=2 ){
  715.           box1->dots=dots=2;um='\"'; // may be the following lines are not quite ok
  716.           while( get_bw(x0,x1,y,y,p,cs,1)==0 &&  y+1<y1) y++; y0=y;
  717.         }
  718.       }
  719.    }
  720. #endif
  721.    dots=box1->dots;  // that does not work for divided letters! ??? change it!
  722.    if(dots) // proof for dots, if not => remove
  723.    {
  724.       // out_x(box1);
  725.       for(y=y0;2*y<y0+y1;y++) if( get_bw(x0,x1,y,y,p,cs,1)==0 ) break; // gap
  726.       if( 2*y>=y0+y1 ){ // not found
  727.         box1->dots=dots=0;
  728.       }
  729.    }
  730.    if( dots>=2 )
  731.    {  // tall box ij"a"o"u
  732.       for(y=y0;2*y<y0+y1;y++) if( get_bw(x0,x1,y,y,p,cs,1)==0 ) break; // gap
  733.       if( 2*y<y0+y1 ){ 
  734.         um='\"'; // may be the following lines are not quite ok
  735.         while( get_bw(x0,x1,y,y,p,cs,1)==0 &&  y+1<y1) y++; y0=y;
  736.       }
  737.    }
  738.    // move upper and lower border (for divided letters)
  739.    while( get_bw(x0,x1,y0,y0,p,cs,1)==0  &&  y0+1<y1) y0++;
  740.    while( get_bw(x0,x1,y1,y1,p,cs,1)==0  &&  y0+1<y1) y1--;
  741.    while( get_bw(x0,x0,y0,y1,p,cs,1)==0  &&  x0+1<x1) x0++;
  742.    while( get_bw(x1,x1,y0,y1,p,cs,1)==0  &&  x0+1<x1) x1--;
  743.    dx=x1-x0+1;
  744.    dy=y1-y0+1;    // size
  745.    box1->x0=x0;box1->y0=y0;    // set reduced frame
  746.    box1->x1=x1;box1->y1=y1;
  747.  
  748.    if( pixel(p,xa,ya)>=cs || 2*ya<y0+y1){    // bad startpoint (from divide)
  749.      for(y=y1;y>=y0;y--) // low to high (not i-dot)
  750.      for(x=x0;x<=x1;x++) 
  751.      if(pixel(p,x,y)<cs){ xa=x;ya=y;y=-1;break; }
  752.    }
  753.  
  754.    // ----- create char-only-box -------------------------------------
  755.    if(dx*dy>MaxBox) return '@';
  756.    static Uchar buf[MaxBox];    // oder 2nd copy of picture, for working
  757.    pix b;
  758.    b.p = buf;
  759.    if( copybox(p,x0,y0,dx,dy,&b,MaxBox) ) return bc;
  760.    // ------ use diagonal too (only 2nd run?) 
  761.    n_run++; mark_nn(b,xa-x0,ya-y0,cs,1); n_run--;
  762.    for(x=0;x<b.x;x++) 
  763.    for(y=0;y<b.y;y++) if(!marked(b,x,y)) b.p[x+y*b.x] = 255&~7;
  764.  
  765.    bc                     =ocr1(box1,b,cs,dots);
  766.    if(bc=='_'){n_run+=1;bc=ocr1(box1,b,cs,dots);n_run-=1;}
  767.    if(bc=='_'){         bc=ocr0(box1,b,cs,dots);         }
  768.    if(bc=='_'){n_run+=1;bc=ocr0(box1,b,cs,dots);n_run-=1;}
  769.    if(bc=='_'){n_run+=2;bc=ocr0(box1,b,cs,dots);n_run-=2;}
  770.    if(bc=='_'){n_run+=3;bc=ocr0(box1,b,cs,dots);n_run-=3;}
  771.  
  772.    if( um=='\"' ){
  773.      if( bc=='a' ) bc=(char)228;
  774.      if( bc=='A' ) bc=(char)196;
  775.      if( bc=='o' ) bc=(char)246;
  776.      if( bc=='O' ) bc=(char)214;
  777.      if( bc=='u' ) bc=(char)252;
  778.      if( bc=='U' ) bc=(char)220;
  779.    }
  780.    // box1->c=bc; out_x(box1); // test
  781.  
  782.    return bc;
  783. }
  784.  
  785.   // ----- detect lines (old version) ---------------
  786. /* suggestion: FT and max. of Amplitude(frequenz) as line-frequence
  787.    option: range for line numbers 1..1000 or similar 
  788.    todo: look for thickest line, and divide if thickness=2*mean_thickness 
  789.  */
  790. int detect_lines1(pix p,int x0,int y0,int dx,int dy,int r){
  791.     int i,j,j2,j3,w,stat=0,y,y2,cs=env.cs; 
  792.     i=lines.num; if(~r)w=16;
  793.     // better function for scanning line arround a letter ???
  794.     // or define lines around known chars "eaTmM"
  795.     w=16;                // width of characters
  796.     for(j2=y=y0;y<y0+dy;y++){
  797.       j=get_line3(x0,x0+dx/2,y,y+lines.dy/2,p,cs,w);
  798.       
  799.       if(stat==1){ // line if stat=1
  800.         if(  j>j2){j2=j;}        /* max. schwarzanteil/zeile */
  801.         if(2*j>j2)lines.m3[i]=y;
  802.         if((j==0  && (y-lines.m3[i])>1+(y-lines.m1[i])/8) || y+1==y0+dy){
  803.           j3=lines.m3[i]-lines.m1[i];
  804.           for(y2=lines.m1[i]+j3/8;y2<lines.m1[i]+j3/2;y2++){
  805.             j=get_line3(x0,x0+dx/2,y2,y2+lines.dy/2,p,cs,w);
  806.             lines.m2[i]=y2; if(17*j>=16*j2) break;    // not the best
  807.           }
  808.           stat=0;j3=lines.m1[i];lines.m4[i]=y;j2=lines.m4[i]-lines.m1[i];
  809.           if(vvv&16)printf(" %2d %2d %2d ",
  810.             lines.m2[i]-j3,lines.m3[i]-j3,lines.m4[i]-j3); /* end of line */
  811.           if(i< MAXlines && j2>7)i++;
  812.           if(i>=MAXlines){ printf("Warning: lines>MAXlines\n");break; }
  813.           j2=0;
  814.         }
  815.       } else {  // empty space between lines
  816.         if(j>w){ // noise ???
  817.           stat=1;lines.m4[i]=lines.m3[i]=lines.m2[i]=lines.m1[i]=y;j2=j;
  818.           lines.x0[i]=x0;lines.x1[i]=x0+dx-1; // ???
  819.           if(vvv&16)printf("\n line= %3d m= %4d",i,y); /* start of line */
  820.         }
  821.       }
  822.     }
  823.     lines.num=i;
  824.     if(vvv)printf(" - lines= %d",lines.num);
  825.     return 0;
  826. }
  827.  
  828. // ----- detect lines via recursiv division (new version) ---------------
  829. int detect_lines2(pix p,int x0,int y0,int dx,int dy,int r){
  830.     int x,y,i,cs=env.cs,x2,y2,x3,y3,x4,y4,x5,y5,y6;
  831.     // shrink box
  832.     if(r>1000){ return -1;} // something is wrong
  833.     if(vvv)printf("\n r=%2d ",r);
  834.  
  835.     // better look for widest h/v-gap
  836.     if(r<8){ // max. depth
  837.       // detect widest horizontal gap
  838.       y2=y3=y4=y5=y6=0; // position and thickness of gap, y6=num_gaps
  839.       for(y=4; y<dy-4; y++){
  840.         if( get_bw2(x0,x0+dx-1,y0+y,y0+y,p,cs,1)==0 ) { y4=y0+y;y5++; }
  841.         else { if(y5>y3){ y3=y5;y2=y4; } 
  842.                // if(y5)printf("\n found y=%3d %3d %3d %3d",y4,y5,y2,y3);
  843.                if(y5)y6++; y5=0; }
  844.       }
  845.       // detect widest vertical gap and divide if there are lot of lines (v-crosses) 
  846.       x2=x3=x4=x5=y5=0;// min. 3 lines
  847.       for(x=1; x<dx-1; x++){
  848.         y=num_cross(x0+x,x0+x,y0,y0+dy-1,p,cs);if(y>y5)y5=y;
  849.         if( y==0 ) { x4=x0+x;x5++; }
  850.         else { if(x5>x3){ x3=x5;x2=x4; } x5=0; }
  851.       }
  852.       i=((x3>2*y3 && dy>5*x3)?1:0);    // good criteria? (KS idea)
  853.       if(vvv && (i || y2))printf(" divide at %s=%4d",((i)?"x":"y"),((i)?x2:y2));
  854.       // divide horizontally if v-gap is thicker than h-gap
  855.       // and length is larger 5*width
  856.       if(i){        detect_lines2(p,x0,y0,x2-x0+1,dy,r+1);
  857.             return  detect_lines2(p,x2,y0,x0+dx-x2+1,dy,r+1); }
  858.       // divide vertically
  859.       i=((y2!=0 && dx>5*y3)?1:0);
  860.       if(i){        detect_lines2(p,x0,y0,dx,y2-y0+1,r+1);
  861.             return  detect_lines2(p,x0,y2,dx,y0+dy-y2+1,r+1);  
  862.       }
  863.     }
  864.  
  865.     if(vvv)printf( " box detected at %4d %4d %4d %4d",x0,y0,dx,dy);
  866.     
  867.     // remove border again!
  868.     for(x=0; x<dx; x++) if( get_bw2(x0+x,x0+x,y0,y0+dy-1,p,cs,1)==1 ) break;
  869.     if(x>0) { x0+=x-1;dx-=x-1; }
  870.     for(x=dx-1;x>0;x--) if( get_bw2(x0+x,x0+x,y0,y0+dy-1,p,cs,1)==1 ) break;
  871.     if(x<dx-1) dx=x+1;
  872.     for(y=0; y<dy; y++) if( get_bw2(x0,x0+dx-1,y0+y,y0+y,p,cs,1)==1 ) break;
  873.     if(y>0) { y0+=y-1;dy-=y-1; }
  874.     for(y=dy-1;y>0;y--) if( get_bw2(x0,x0+dx-1,y0+y,y0+y,p,cs,1)==1 ) break;
  875.     if(y<dy-1) dy=y+1;
  876.     if(dx<5 || dy<7) return 0; // do not care about dust
  877.     return detect_lines1(p,x0-1,y0-2,dx+2,dy+3,r+1);
  878.  
  879.     i=lines.num; lines.num++;
  880.     lines.m1[i]=y0;          lines.m2[i]=y0+5*dy/16;
  881.     lines.m3[i]=y0+12*dy/16; lines.m4[i]=y0+dy-1;
  882.     lines.x0[i]=x0;          lines.x1[i]=x0+dx-1;
  883.     if(vvv)printf(" - line= %d",lines.num);
  884.     return 0;
  885. }
  886.  
  887. // test if char c in string cset
  888. int strc(char c, char *cset){
  889.   for(int i=0;cset[i] && i<10;i++)
  890.     if(cset[i]==c) return 1;
  891.   return 0;
  892. }
  893.  
  894. void help(){
  895.     printf( " Optical Character Recognition gocr "Version"\n options:\n"
  896.             " -i name   - image file (pnm,pgm,pbm,ppm,pcx)\n"
  897.             " -i -      - read PNM from stdin (djpeg -pnm -gray a.jpg | gocr -i -)\n"
  898.             " -l num    - grey level 0<160<=255\n"
  899.             " -d num    - dust_size (remove all smaller clusters, 10=default)\n"
  900.             " -s num    - spacewidth/dots (0 = autodetect)\n"
  901.             " -v num    - verbose  [summed]\n"
  902.             "      1      print more info\n"
  903.             "      2      list chapes  of boxes (see -c)\n"
  904.             "      4      list pattern of boxes (see -c)\n"
  905.             "      8      print pattern after recognition\n"
  906.             "     16      print line infos\n"
  907.             "     32      debug outXX.pgm\n"
  908.             " -c string - list of chars (_ = not recognized chars)\n"
  909.             " -m num    - operation modes, ~ = switch off\n"
  910.             "      2      use database (development)\n"
  911.             "      4      layout analysis (testing)\n"
  912.             "      8      ~ compare non recognized chars\n"
  913.             "     16      ~ divide overlapping chars\n"
  914.             "     32      ~ context correction\n"
  915.             "     64      char packing\n"
  916.             " examples: gocr -v 6 -v 32 -c _YV -i text1.pbm\n"
  917.             "           djpeg -pnm -gray text.jpg | gocr -i -\n"
  918.             "\n");
  919.     exit(0);
  920. }
  921.  
  922. // load database into boxd-chain
  923. // this is added in version v0.2.4 
  924. //   as alternate engine comparing chars with database
  925. int load_db(){
  926.   FILE *f1; char s1[80],s2[80]="./db/"; int i,j,ii;
  927.   if(vvv)printf("# load database ./db/ ... ");
  928.   f1=fopen("db/db.lst","r");if(!f1){printf(" DB not found\n");return 1;}
  929.   for(ii=0;!feof(f1);ii++){
  930.     fgets(s1,80,f1);j=strlen(s1);if(j==0)continue;
  931.     for(i=0;i<j && strc(s1[i]," \t,;")==0;i++)s2[5+i]=s1[i];
  932.     /* struct pix *pp=new struct pix(); gcc2.95.2 error */
  933.     pix *pp=new pix();
  934.     readpgm(s2,pp,0*vvv);
  935.       struct box *box3=new struct box();
  936.       box3->x0=1;      box3->x1=pp->x-3;  // white border 1 pixel width
  937.       box3->y0=1;      box3->y1=pp->y-3;
  938.       box3->x=1;       box3->y=1;
  939.       box3->dots=0;    box3->c=s1[i+1];
  940.       box3->next=box3->pre=NULL;
  941.       box3->num=0;     box3->line=-1;
  942.       box3->m1=0;box3->m2=0;box3->m3=0;box3->m4=0;
  943.       box3->p=pp;
  944.       box_app(&boxd,box3);        // append to list
  945.   }
  946.   fclose(f1);if(vvv)printf(" %d chars loaded\n",ii);
  947.   return 0;
  948. }
  949.  
  950. char ocr_db(struct box *box1){
  951.   int d,dist=1000; char c='_'; struct box *box2,*box3;
  952.   for(box3=box2=boxd;box2;box2=box2->next){
  953.     // do preselect!!! distance() slowly
  954.     d=distance2(*(box2->p),box2,*(box1->p),box1,env.cs);
  955.     if(d<dist){ dist=d; c=box2->c; box3=box2; }
  956.   }
  957.   if(vvv)printf("\n# db dist=%4d c=%c",dist,c);
  958.   if(dist>30) c='_'; 
  959.   else {
  960.     vvv|=1024; // verbose output for tests
  961.     // d=distance2(*(box3->p),box3,*(box1->p),box1,env.cs);
  962.     vvv&=~1024;
  963.   }
  964.   return c;
  965. }
  966.  
  967. // -------------------------------------------------------------
  968. // ------             MAIN 
  969. // -------------------------------------------------------------
  970. int main(int argn, char *argv[]){
  971.   int i,x,y,cs=0,spc=0,plothisto=0,mo=0,dust_size=10;
  972.   int sumX=0,sumY=0;        // average size => sumX/numC,sumY/numC
  973.   int numC=0;
  974.   pix p,p2;
  975.   env.cs=160; env.avX=5; env.avY=8; env.p=NULL; // default values
  976.   char *inam="text.pgm",cc=0,*lc="_",*s1;
  977.   setvbuf(stdout,(char *)NULL,_IONBF,0); // not buffered
  978.   for(i=1;i<argn;i++){
  979.     if( argv[i][0]=='-' ) { cc=argv[i][1]; }
  980.     s1="";if(i+1<argn)s1=argv[i+1];
  981.     switch ( cc ) {
  982.      case 'h' : help(); break;
  983.      case 'i' : inam =  s1;i++; break;
  984.      case 'c' : lc   =  s1;i++; break;
  985.      case 'd' : dust_size  =  atoi(s1);i++; break;
  986.      case 'l' : cs  =  atoi(s1);i++; break;
  987.      case 's' : spc =  atoi(s1);i++; break;
  988.      case 'v' : vvv |= atoi(s1);i++; break;
  989.      case 'm' : mo  |= atoi(s1);i++; break;
  990.      default: printf("# unknown option use -h for help\n");
  991.     }
  992.     cc=0;
  993.   }
  994.   if(!cs) cs =128+32; env.cs=cs;
  995.   if(vvv)setvbuf(stdout,NULL,_IONBF,0);
  996.   if(vvv)
  997.   printf("# options are: -i %s -l %d -s %d -v %d -c %s -m %d\n",
  998.              inam,cs,spc,vvv,lc,mo);
  999.   // ----- read picture
  1000.   for(i=0;inam[i]!=0 && i<200;i++);    // string-length
  1001.   if(i>3 && inam[i-4]=='.'
  1002.          && inam[i-3]=='p'
  1003.          && inam[i-2]=='c'
  1004.          && inam[i-1]=='x') { readpcx(inam,&p,vvv); }
  1005.   else                      { readpgm(inam,&p,vvv); }
  1006.   env.p=&p;
  1007.   { 
  1008.     p2.p = new Uchar[ p.y*p.x ];     // buffer
  1009. //  p2.p = (Uchar *)malloc(dy*dx);
  1010.     assert(p2.p);
  1011.     copybox(p,0,0,p.x,p.y,&p2,p.x*p.y);
  1012.   }
  1013.   // ----- count colors ------ create histogram -------
  1014.   Uint col[256];
  1015.   makehisto(p,col,plothisto);
  1016.   if(mo&2) load_db();
  1017.  
  1018.   // this is first step for reorganize the PG
  1019.   // ---- look for letters, put rectangular frames arround letters
  1020.   // letter = connected points near color F
  1021.   // should be used by dust removing (faster) and line detection!
  1022.   // ---- 0..cs = black letters, last change = Mai99
  1023.   {
  1024.     clr_bits(p,0,p.x-1,0,p.y-1);
  1025.     if(vvv)printf("# scanning boxes");
  1026.     for(y=0; y<p.y;y++) // better: rekursiv nn-pixel suchen
  1027.     for(x=0; x<p.x;x++)
  1028.     {
  1029.       if( marked(p,x,y)     ) continue;    // marked
  1030.       if( pixel (p,x,y)>=cs ) continue;    // no pixel
  1031.       int x0=x,x1=x,y0=y,y1=y,dots=0;    // box
  1032.  
  1033.       frame_nn(p,x,y,x0,x1,y0,y1,cs,AT);    // frame and mark nn-dots
  1034.       p.p[x+y*p.x]|=M1;            // mark startpoint
  1035.       numC++;
  1036.  
  1037.       // --- insert in list
  1038.       struct box *box3=new struct box();
  1039.       box3->x0=x0;     box3->x1=x1;
  1040.       box3->y0=y0;     box3->y1=y1;
  1041.       box3->x=x;       box3->y=y;
  1042.       box3->dots=dots; box3->c=(((y1-y0+1)*(x1-x0+1)>=MaxBox)?'@':'_');
  1043.       box3->next=box3->pre=NULL;
  1044.       box3->num=numC;
  1045.       box3->line=0;    // not used here
  1046.       box3->m1=0; box3->m2=0; box3->m3=0; box3->m4=0;
  1047.       box3->p=&p;
  1048.       box_app(&box1,box3);        // append to list
  1049.     }
  1050.     if(numC){ if(vvv)printf(" %d\n",numC); }
  1051.   }
  1052.  
  1053.   /* ---- remove dust ---------------------------------
  1054.      What is dust? I think, this is a very small pixel cluster without
  1055.      neighbours. Of course not all dust clusters can be detected correct.
  1056.      This feature should be possible to switch off via option.
  1057.      -> may be, all clusters should be stored here?
  1058.      speed is very slow, I know, but I am happy that it is working well
  1059.   */
  1060.   // new dust removing
  1061.   int ds,df=100000,dd; // dust found
  1062.   i=df-1; // dustsize should be revers proportional to its number
  1063.   dd=dust_size/8+1;    // step
  1064.   struct box *box3;
  1065.   for( ds=1;i<df && ds<dust_size && i>0;ds+=dd )
  1066.   { int x,y,j;df=i;i=0;
  1067.     if(vvv){ printf("# searching dust size<%2d",ds+1);
  1068.              if(ds>2)printf(" and remove detected dust");
  1069.              printf(" ..."); 
  1070.     }
  1071.     for(i=0,box2=box1;box2;box2=box2->next){
  1072.       int x0=box2->x0,x1=box2->x1,y0=box2->y0,y1=box2->y1;    // box
  1073.  
  1074.       j=0; /* count pixel */
  1075.       for(x=x0;x<=x1;x++)
  1076.       for(y=y0;y<=y1;y++){
  1077.         if( pixel(p,x,y)<cs ){ j++; }
  1078.       }
  1079.       box2->dots=j; // temporaly used
  1080.     }
  1081.     for(i=0,box2=box1;box2;box2=box2->next){
  1082.       int x0=box2->x0,x1=box2->x1,y0=box2->y0,y1=box2->y1;    // box
  1083.       j=box2->dots;
  1084.       if(j<=ds-dd)      /* remove this */
  1085.       { numC--;
  1086.         for(x=x0;x<=x1;x++)
  1087.         for(y=y0;y<=y1;y++){ put(p,x,y,0,255&~7); }
  1088.         box3=box2->pre;box_del(box2);delete box2;box2=box3;
  1089.         if(!box2)box2=box1; // this is hopefully right
  1090.         continue;
  1091.       }
  1092.       if(j<=ds  ) i++; /* count as dust particle */
  1093.     }
  1094.     if(vvv)printf(" %3d cluster detected\n",i);
  1095.   }
  1096.   if(vvv)printf("# remaining boxes %d\n",numC);
  1097.  
  1098.   lines.dy=0; lines.num=0;  // meanvalue of rise
  1099.   // ----- detect longest lines, is it horizontal? ---------------
  1100.   { int dy=0,j,y2,k; 
  1101.     if(vvv)printf( "# detect longest line"); // or read from outside???
  1102.     // most black/white changes ???
  1103.     for(y2=i=y=0;y<p.y;y+=4){ 
  1104.       j=num_cross(0,p.x-1,y,y,p,cs); if(j>i){ i=j; y2=y; } 
  1105.     }
  1106.     if(vvv)printf(" - at y=%d crosses=%3d",y2,i);
  1107.     if(y2>100) // better only on long lines
  1108.     for(k=0;k<30;k++)    // change angle, good algo???
  1109.     for(y=y2-15;y<y2+15;y++){
  1110.       j=num_cross(0,p.x-1,y,y+k,p,cs); if(j>i){ i=j; y2=y; dy= k-1; }
  1111.       j=num_cross(0,p.x-1,y,y-k,p,cs); if(j>i){ i=j; y2=y; dy=-k+1; }
  1112.     }
  1113.     lines.dy=dy;
  1114.     if(vvv)printf(" - at y=%d crosses=%3d dy=%d\n",y2,i,dy);
  1115.     { dy=lines.dy;
  1116.       if(vvv&32){
  1117.         for(y=0;y<p.y;y++)for(x=0;x<p.x;x++)p2.p[x+p.x*y]=p.p[x+p.x*y]&(192);
  1118.         for(x=0;x<p2.x;x++)if((x&35)>32)put(p2,x,y2+dy*x/p2.x,255,32);
  1119.         // writebmp("out10.bmp",p2,vvv); // colored should be better
  1120.       }
  1121.     }
  1122.   }
  1123.  
  1124.  
  1125.   // ----- detect lines ---------------
  1126.   if(vvv)printf( "# scanning lines "); // or read from outside???
  1127.   if(mo&4) detect_lines2(p,0,0,p.x,p.y,0); // later replaced by better algo
  1128.   else     detect_lines1(p,0,0,p.x,p.y,0); // old algo
  1129.   { int dy=lines.dy;
  1130.     if(vvv&32){
  1131.       for(i=0;i<lines.num;i++){ // mark lines
  1132.         for(x=lines.x0[i];x<lines.x1[i];x++){
  1133.           y=lines.m1[i];if((x& 7)==4)put(p2,x,y+dy*x/p2.x,255,32);
  1134.           y=lines.m2[i];if((x& 3)==2)put(p2,x,y+dy*x/p2.x,255,32);
  1135.           y=lines.m3[i];if((x& 1)==1)put(p2,x,y+dy*x/p2.x,255,32);
  1136.           y=lines.m4[i];if((x& 7)==4)put(p2,x,y+dy*x/p2.x,255,32);
  1137.         }
  1138.       }
  1139.       // writebmp("out10.bmp",p2,vvv); // colored should be better
  1140.     }
  1141.   }
  1142.   // erase box list, temporarely (later sorting and gluing)
  1143.   for(;box1;){
  1144.     box2=box1; box_del(box2); delete box2;
  1145.   }
  1146.   numC=0;
  1147.  
  1148.   // ---- clear last 3 bits (flag)
  1149.   clr_bits(p,0,p.x-1,0,p.y-1);
  1150.  
  1151.   // ---- look for letters, put rectangular frames arround letters
  1152.   // letter = connected points near color F
  1153.   if(vvv)printf("\n# scanning boxes");
  1154.   for(i=0;i<lines.num;i++)    // nur erkannte Zeilen
  1155.   {
  1156.     for(x=lines.x0[i]; x<=lines.x1[i];x++)
  1157.     for(y=lines.m3[i];2*y>lines.m2[i]+lines.m1[i];y--) // better: rekursiv nn-pixel suchen
  1158.     {
  1159.       int m1=lines.m1[i]+lines.dy*x/p.x;
  1160.       int m2=lines.m2[i]+lines.dy*x/p.x;
  1161.       int m3=lines.m3[i]+lines.dy*x/p.x;
  1162.       int m4=lines.m4[i]+lines.dy*x/p.x;
  1163.       if( marked(p,x,y)     ) continue;    // marked
  1164.       if( pixel (p,x,y)>=cs ) continue;    // no pixel
  1165.       int x0=x,x1=x,y0=y,y1=y,dots=0;    // box
  1166.  
  1167.       frame_nn(p,x,y,x0,x1,y0,y1,cs,AT);    // frame and mark nn-dots
  1168.       p.p[x+y*p.x]|=M1;            // mark startpoint
  1169.  
  1170.       if(2*y0>m2+m3 || y1-y0<5){  // .-, test :=;!? better explicite dot-proof-func
  1171.          int x,y;
  1172.          for(x=x0;x<x1+2;x++)for(y=m2;y<y0;y++)
  1173.          if(!marked(p,x,y))if(pixel(p,x,y)<cs && pixel(p,x+(x1-x0)/8,y)<cs)
  1174.          frame_nn(p,x,y,x0,x1,y0,y1,cs,AT); // expand frame box
  1175.       }
  1176.       if(2*y0>m1+m2){  // aeiou test umlaut \"a\"o\"ui
  1177.          // shrink white frame
  1178.          int x,y,x2,y2,y3=y0;y2=m1;x2=x1+1;
  1179.          if(x2-x0>4 && 2*(x1-x0)>y1-y0) x2=x1-1;
  1180.          if(y1-y0>4 && y0-(y1-y0)/2>y2) y2=y0-(y1-y0)/2;
  1181.          for(x=x0;x<x2;x++)for(y=y2;y<y3;y++)
  1182.          if(!marked(p,x,y))if(pixel(p,x,y)<cs && pixel(p,x+(x1-x0)/8,y)<cs)
  1183.          { frame_nn(p,x,y,x0,x1,y0,y1,cs,AT);dots++; } // expand frame box
  1184.          // printf(" dots=%d\n",dots);out_b(p,x0,y0,x1-x0+1,y1-y0+1,cs);
  1185.       }
  1186.       if(2*y0<m1+m2 && 2*y1<m3+m4)
  1187.       if(  y1<m3-(y1-y0)/16 ){              // test !?
  1188.          int x,y,y2;y2=m3;if(y2>y1+(y1-y0)/2)y2=y1+(y1-y0)/2;
  1189.          for(x=x0;x<x1-2;x++)for(y=y1;y<y2;y++)
  1190.          if(!marked(p,x,y))if(pixel(p,x,y)<cs)
  1191.          frame_nn(p,x,y,x0,x1,y0,y1,cs,AT); // expand frame box
  1192.       }
  1193.       if( (y1-y0+1)*(x1-x0+1)<5 )                // points arround ???
  1194.       if( 2*y1<m3+m2) continue;    // dust 
  1195.       numC++; sumY+=y1-y0+1; sumX+=x1-x0+1;
  1196.       // --- insert in list
  1197.   #if 0
  1198.       struct box *box3=new struct box
  1199.         ((struct box){x0,x1,y0,y1,x,y,dots,(struct box*)NULL,(struct box*)NULL,
  1200.         '_',numC,i,
  1201.         lines.m1[i]+lines.dy*x/p.x,
  1202.         lines.m2[i]+lines.dy*x/p.x,
  1203.         lines.m3[i]+lines.dy*x/p.x,
  1204.         lines.m4[i]+lines.dy*x/p.x,&p});
  1205.   #else
  1206.       struct box *box3=new struct box();
  1207.       box3->x0=x0;     box3->x1=x1;
  1208.       box3->y0=y0;     box3->y1=y1;
  1209.       box3->x=x;       box3->y=y;
  1210.       box3->dots=dots; box3->c=(((y1-y0+1)*(x1-x0+1)>=MaxBox)?'@':'_');
  1211.       box3->next=box3->pre=NULL;
  1212.       box3->num=numC;  box3->line=i;
  1213.       box3->m1=lines.m1[i]+lines.dy*x/p.x;
  1214.       box3->m2=lines.m2[i]+lines.dy*x/p.x;
  1215.       box3->m3=lines.m3[i]+lines.dy*x/p.x;
  1216.       box3->m4=lines.m4[i]+lines.dy*x/p.x;
  1217.       box3->p=&p;
  1218.   #endif
  1219.       if(box3->y1-box3->y0 >= box3->m3-box3->m2){ // letter
  1220.         int dx=0; // correction of obviously wrong lines.dy
  1221.         if(box3->y1 > box3->m4) dx=box3->y1 - box3->m4;
  1222.         if(box3->y1 < box3->m3) dx=box3->y1 - box3->m3;
  1223.         if(box3->y0 > box3->m2) dx=box3->y0 - box3->m2;
  1224.         if(box3->y0 < box3->m1) dx=box3->y0 - box3->m1;
  1225.         box3->m1+=dx;   box3->m2+=dx;
  1226.         box3->m3+=dx;   box3->m4+=dx;
  1227.       }
  1228.       box_app(&box1,box3);        // append to list
  1229.   #if 0
  1230.       int y2,y3;   // i-dot but no "Vo" overlap
  1231.       y3=((y1-y0>x1-x0)?y1-y0:x1-x0);
  1232.       y2=lines.m1[i]+lines.dy*x/p.x;if(y0-y2>y3/4)y2=y0-y3/4;if(y3<5)y2=y0-7;
  1233.       for(y=y2;y< y0;y++)if(get_bw(x0+(x1-x0)/4,x1-1,y,y,p,cs,1)){ box3->y0=y;break; }
  1234.       y2=lines.m3[i]+lines.dy*x/p.x;if(y2-y1>y3/4)y2=y1+y3/4;if(y3<5)y2=y1+5;
  1235.       for(y=y2;y>=y1;y--)if(get_bw(x0+1,x1-1,y,y,p,cs,1)){ box3->y1=y;break; }
  1236.       x=x1+1; break;
  1237.   #endif
  1238.     }
  1239.   }
  1240.   if(numC){  env.avY=sumY/numC; env.avX=sumX/numC;
  1241.     if(vvv)printf(" %d - average X Y %d %d\n",numC,sumX/numC,sumY/numC);
  1242.   }
  1243.  
  1244.   // ---- analyse boxes, find pictures (do this first!!!)
  1245.   if(vvv)printf("# detect pictures");
  1246.   for(i=0,box2=box1;box2;box2=box2->next){
  1247.     int x0=box2->x0,x1=box2->x1,y0=box2->y0,y1=box2->y1;    // box
  1248.  
  1249.     if( x1-x0+1>10*env.avX || y1-y0+1>10*env.avY )    /* large picture */
  1250.     { sumX-=x1-x0+1; sumY-=y1-y0+1; numC--; box2->c='@'; }
  1251.     if( box2->c=='@' ) i++;
  1252.  
  1253.     if( 4*(y1-y0+1)<env.avY || y1-y0<2)    // dots .,-_ etc.
  1254.     { sumX-=x1-x0+1; sumY-=y1-y0+1; numC--; }    // better after pictures!
  1255.   }
  1256.   if(!numC){ printf("\n no chars found - stopped\n");exit(1); }
  1257.   env.avY=sumY/numC; env.avX=sumX/numC;
  1258.   if ( spc==0 ) spc = (env.avX+20) / 4;
  1259.   if(vvv)printf(" %d - boxes %d",i,numC);
  1260.   if(vvv)printf(" - new average X Y %d %d spc %d\n",env.avX,env.avY,spc);
  1261.  
  1262.  
  1263.  
  1264.   // ---- analyse boxes, compare chars, compress picture ------------
  1265.   // ToDo: - error-correction only on large chars! 
  1266.   if((mo&64)){
  1267.     if(vvv)printf("# packing");
  1268.     for(i=0,box2=box1;box2;box2=box2->next,i++);        // count boxes
  1269.     for(box2=box1;box2;box2=box2->next){
  1270.       struct box *box3,*box4=box2,*box5;
  1271.       int dist=1000,n1;    // 100% maximum
  1272.       int dx = box2->x1 - box2->x0 + 1;
  1273.       if(vvv)printf("\r# packing %5d",i);
  1274.       if( dx>3 )
  1275.       for(box3=box2->next;box3;box3=box3->next)if(box2->num!=box3->num){
  1276.         int d=distance(p,box2,p,box3,cs);
  1277.         if ( d<dist ) { dist=d; box4=box3; }    // best fit
  1278.         if ( d<5 ){   // good limit = 5% ??? 
  1279.           i--;n1=box3->num;        // set all num==box2.num to box2.num
  1280.           for(box5=box1;box5;box5=box5->next)if(box5!=box2)
  1281.             if( box5->num==n1 ) box5->num=box2->num;
  1282.           // out_b2(p,box2->x0,box2->y0,dx,dy,cs,box5->x0,box5->y0);
  1283.           // printf(" dist=%d\n",d);
  1284.         }
  1285.       }
  1286.       // nearest dist to box2 has box4
  1287.       //    out_b2(p,box2->x0,box2->y0,dx,dy,cs,box4->x0,box4->y0);
  1288.       //    printf(" dist=%d\n",dist); 
  1289.     }
  1290.     int k=0;
  1291.     if(vvv)printf(" %d different chars",i);
  1292.     for(box2=box1,i=0;box2;box2=box2->next){
  1293.       struct box *box3,*box4;
  1294.       int j,dist;
  1295.       for(box3=box1;box3!=box2 && box3!=NULL;box3=box3->next)
  1296.       if(box3->num==box2->num)break;
  1297.       if(box3!=box2 && box3!=NULL)continue;
  1298.       i++;
  1299.       // count number of same chars
  1300.       dist=0;box4=box2;
  1301.       for(box3=box2,j=0;box3;box3=box3->next)if(box3->num==box2->num){
  1302.         j++;
  1303.         int d=distance(p,box2,p,box3,cs);
  1304.         if ( d>dist ) { dist=d; box4=box3; }    // worst fit
  1305.       }
  1306.       if(vvv&8){
  1307.         out_b2(p,box2->x0,box2->y0,box2->x1-box2->x0+1,box2->y1-box2->y0+1,cs,
  1308.                  box4->x0,box4->y0);
  1309.         printf(" no %d char %4d %5d times maxdist=%d\n",i,box2->num,j,dist);
  1310.       }
  1311.       // calculate mean-char (error-correction)
  1312.       // ToDo: calculate maxdist in group 
  1313.       k+=j;
  1314.   //    if(j>1)
  1315.   //    out_b(p,box2->x0,box2->y0,box2->x1-box2->x0+1,box2->y1-box2->y0+1,cs);
  1316.       if(vvv&8)
  1317.       printf(" no %d char %4d %5d times sum=%d\n",i,box2->num,j,k);   
  1318.     }
  1319.     if(vvv)printf(" ok\n");
  1320.   }
  1321.  
  1322.  
  1323.   // ---- analyse boxes, find chars ---------------------------------
  1324.   if(vvv)printf("# step 1: char recognition");
  1325.   for(i=0,box2=box1;box2;box2=box2->next){
  1326.     struct box *box3;
  1327.     int x0=box2->x0,x1=box2->x1,
  1328.         y0=box2->y0,y1=box2->y1;    // box
  1329.  
  1330.     char cc=box2->c;  // only makes sence on gray-pictures!
  1331.     if(cc=='_') cc=whatletter(box2,cs   ); // 90%
  1332. //    if(cc=='_') cc=whatletter(box2,cs-20); // 60%
  1333.     if(mo&2) if(cc=='_') cc=ocr_db(box2);
  1334.     if(vvv&8) { printf("%c\n",cc); 
  1335.                 out_b(p,x0,y0,x1-x0+1,y1-y0+1,cs); }
  1336.     
  1337.     box2->c=cc; if(cc=='_')    // copy char
  1338.     for(box3=box1;box3;box3=box3->next)if(box3->num==box2->num)box3->c=cc;
  1339.     if( cc=='_' ) i++; 
  1340.  
  1341.   }
  1342.   if(vvv)printf(", %d chars unidentified\n",i);
  1343.  
  1344.   // ----------- write out20.pgm -----------
  1345.   if(vvv&32){
  1346.     for(box2=box1;box2;box2=box2->next){
  1347.       for(y=box2->y0;y<=box2->y1;y++)
  1348.       for(x=box2->x0;x<=box2->x1;x++){
  1349.        if( x==box2->x0 || x==box2->x1
  1350.         || y==box2->y0 || y==box2->y1 ) p2.p[x+y*p.x]|=32;  // box
  1351.        if( x> box2->x0 && x< box2->x1
  1352.         && y> box2->y0 && y< box2->y1 )
  1353.        if(box2->c=='_' && (1&(x+y))!=0) p2.p[x+y*p.x]|=16;  // box
  1354.       }
  1355.     } 
  1356.     // writepgm("out20.pgm",p2);
  1357.     writebmp("out20.bmp",p2,vvv); // colored should be better
  1358.     // for(y=0;y<p.y;y++)for(x=0;x<p.x;x++)p2.p[x+p.x*y]=p.p[x+p.x*y];
  1359.   }
  1360.  
  1361.   // ---- ------------------------------- 
  1362.   if(vvv)printf("# step 2: try to compare unknown with known chars");
  1363.   if(!(mo&8))
  1364.   for(i=0,box2=box1;box2;box2=box2->next)if(box2->c=='_'){
  1365.     struct box *box3,*box4=box1; int dist=1000,d; // 100% maximum
  1366.     char bc='_';        // best fit char
  1367.     for(box3=box1;box3;box3=box3->next)if(box3!=box2)if(box3->c!='_'){
  1368.       d=distance(p,box2,p,box3,cs);
  1369.       if ( d<dist ) { dist=d; bc=box3->c; box4=box3; }
  1370.     }
  1371.     if(dist<10) { box2->c=bc;i++; }    // limit as option???
  1372.                 //  => better max distance('e','e') ???
  1373.     if( dist<50 && (vvv&7)){  // only for debugging
  1374.      printf("\n# L%02d best fit was %c %3d%% %d",box2->line,bc,dist,i);
  1375.      out_x2(box2,box4);
  1376.     }
  1377.   } 
  1378.   if(vvv)printf(" - found %d\n",i);
  1379.  
  1380.   // ---- divide overlapping chars which !in_str(c,"_,.:;");
  1381.   // completely changed at Mar2000
  1382.   // division if dots>0 does not work proper! ???
  1383.   //
  1384.   // what about glued be?
  1385.   // what about rekursiv division?
  1386.   if(vvv)printf("# step 3: try to divide unknown chars");
  1387.   if(!(mo&16))
  1388.   for(box2=box1;box2;box2=box2->next)
  1389.   if(box2->c=='_' && box2->x1-box2->x0>5 && box2->y1-box2->y0>4){
  1390.       struct box *box3;
  1391.       struct box boxa,boxb,boxc;    // temporarely needed boxes 
  1392.       char c1,c2,c3,*s1="_.,'!;?:-=()";    // not accepted chars
  1393.       int k2,x0,x1,y0,y1,x=0,x2=0;c1=c2=c3='_';
  1394.       x0=box2->x0; x1=box2->x1;
  1395.       y0=box2->y0; y1=box2->y1;
  1396.       // one vertical line can not be two glued chars
  1397.       if( num_cross(x0,x1,(y1+y0)/2,(y1+y0)/2,p,cs)>1 )
  1398.       {    // doublet = 2 letters
  1399.         int i,ii,j,k,m,m1,m2,m3,i1,i2,i3,dx=(x1-x0)/64,dy=(y1-y0+1);
  1400.         if(vvv&32)out_b(p,x0,y0,x1-x0+1,y1-y0+1,cs);
  1401.         m1=m2=m3=0; i1=i2=i3=0; // searching minima m1 m2 m3
  1402.         // it would be better if testing is only if most right and left char
  1403.         //   is has no horizontal gap (below m2) ex: be
  1404.         for(i=0;i<(x1-x0)/2-2;i++)   // rm <=> nn .@ mask? for better sorting
  1405.         for(ii=-1;ii<2;ii+=((i)?2:4)){
  1406.           k2=0;
  1407.           for(m=-i/8,j=y0;j<=y1;j++){
  1408.             k=((pixel(p,(x1+x0)/2+ii*i,j)<cs)?0:1); m+=4*k; // using gray ???
  1409.             if(!k) m+=((pixel(p,(x1+x0)/2+ii*i-1,j)<cs)?0:2);
  1410.             if(!k) m+=((pixel(p,(x1+x0)/2+ii*i+1,j)<cs)?0:2);
  1411.             if(!k) m+=((pixel(p,(x1+x0)/2+ii*i-2,j)<cs)?0:1);
  1412.             if(!k) m+=((pixel(p,(x1+x0)/2+ii*i+2,j)<cs)?0:1);
  1413.             if(k!=k2) m-=dy/2; k2=k;  // many b/w changes are bad!
  1414.           }
  1415.           // replace one of 3 maxima (nearest or lowest
  1416.           if( abs(i3-ii*i)<2+dx ){ if(m>m3) { m3=m;i3= ii*i; } } else
  1417.           if( abs(i2-ii*i)<2+dx ){ if(m>m2) { m2=m;i2= ii*i; } } else
  1418.           if( abs(i1-ii*i)<2+dx ){ if(m>m1) { m1=m;i1= ii*i; } } else
  1419.                                  { if(m>m3) { m3=m;i3= ii*i; } }
  1420.           // sort it
  1421.           if( m3>m2 ){ k=m2;m2=m3;m3=k; k=i2;i2=i3;i3=k; }
  1422.           if( m2>m1 ){ k=m1;m1=m2;m2=k; k=i1;i1=i2;i2=k; }
  1423.           if( m3>m2 ){ k=m2;m2=m3;m3=k; k=i2;i2=i3;i3=k; }
  1424.  
  1425.         }
  1426.         i1+=(x1+x0)/2;
  1427.         i2+=(x1+x0)/2;
  1428.         i3+=(x1+x0)/2;
  1429.         if(vvv&32)printf(" i1,i2,i3 m123= %d %d %d  %d %d %d\n",i1-x0,i2-x0,i3-x0,m1,m2,m3);
  1430.         // removing ->dots if dot only above one char !!! ??? not implemented
  1431.         if( 2*m1>y1-y0 ) // minimum of white pixels should be found
  1432.         {
  1433.           boxa=*box2;boxb=*box2,boxc=*box2;    // copy contents
  1434.           boxa.next=&boxb;boxb.pre=&boxa;   // new pointers
  1435.           x=i1;
  1436.           boxa.x=x0; boxa.y=y0;boxa.x1=x;
  1437.           boxb.x=x+1;boxb.y=y0;boxb.x0=x+1;
  1438.           c1=whatletter(&boxa,cs); // unknown startpos!
  1439.           c2=whatletter(&boxb,cs);
  1440.       // boxa..c changed!!! dots should be modified!!!
  1441.           if( in_str(c1,s1)  || in_str(c2,s1) ) x=0;
  1442.           if(vvv&32)printf(" x c12 =%d %c %c\n",x-x0,c1,c2);
  1443.         }
  1444.         if( 2*m2>y1-y0 )
  1445.         if(!x){
  1446.           boxa=*box2;boxb=*box2,boxc=*box2;    // copy contents
  1447.           boxa.next=&boxb;boxb.pre=&boxa;   // new pointers
  1448.           x=i2;
  1449.           boxa.x=x0; boxa.y=y0;boxa.x1=x;
  1450.           boxb.x=x+1;boxb.y=y0;boxb.x0=x+1;
  1451.           c1=whatletter(&boxa,cs); // unknown startpos!
  1452.           c2=whatletter(&boxb,cs);
  1453.           if( in_str(c1,s1)  || in_str(c2,s1) ) x=0;
  1454.           if(vvv&32)printf(" x c12 =%d %c %c\n",x-x0,c1,c2);
  1455.         }
  1456.         if( 2*m3>y1-y0 )
  1457.         if(!x){
  1458.           boxa=*box2;boxb=*box2,boxc=*box2;    // copy contents
  1459.           boxa.next=&boxb;boxb.pre=&boxa;   // new pointers
  1460.           x=i3;
  1461.           boxa.x=x0; boxa.y=y0;boxa.x1=x;
  1462.           boxb.x=x+1;boxb.y=y0;boxb.x0=x+1;
  1463.           c1=whatletter(&boxa,cs); // unknown startpos!
  1464.           c2=whatletter(&boxb,cs);
  1465.           if( in_str(c1,s1)  || in_str(c2,s1) ) x=0;
  1466.           if(vvv&32)printf(" x c12 =%d %c %c\n",x-x0,c1,c2);
  1467.         }
  1468.         if( 2*m2>y1-y0 )
  1469.         if(!x){
  1470.           boxa=*box2;boxb=*box2,boxc=*box2;    // copy contents
  1471.           boxa.next=&boxb;boxb.pre=&boxa;   // new pointers
  1472.           x2=i2; x=i1; if(x>x2){ k=x;x=x2;x2=k; }
  1473.           boxa.x=x0;  boxa.y=y0;boxa.x1=x;
  1474.           boxb.x=x+1; boxb.y=y0;boxb.x0=x+1;boxb.x1=x2;
  1475.           boxc.x=x2+1;boxc.y=y0;boxc.x0=x2+1;
  1476.           c1=whatletter(&boxa,cs); // unknown startpos!
  1477.           c2=whatletter(&boxb,cs);
  1478.           c3=whatletter(&boxc,cs);
  1479.           if( in_str(c1,s1)  || in_str(c2,s1)   || in_str(c3,s1) ) x=0;
  1480.           if(vvv&32)printf(" x c123=%d %c %c %c\n",x-x0,c1,c2,c3);
  1481.         }
  1482.         if( 2*m3>y1-y0 )
  1483.         if(!x){
  1484.           boxa=*box2;boxb=*box2,boxc=*box2;    // copy contents
  1485.           boxa.next=&boxb;boxb.pre=&boxa;   // new pointers
  1486.           x2=i3; x=i1; if(x>x2){ k=x;x=x2;x2=k; }
  1487.           boxa.x=x0;  boxa.y=y0;boxa.x1=x;
  1488.           boxb.x=x+1; boxb.y=y0;boxb.x0=x+1;boxb.x1=x2;
  1489.           boxc.x=x2+1;boxc.y=y0;boxc.x0=x2+1;
  1490.           c1=whatletter(&boxa,cs); // unknown startpos!
  1491.           c2=whatletter(&boxb,cs);
  1492.           c3=whatletter(&boxc,cs);
  1493.           if( in_str(c1,s1)  || in_str(c2,s1)   || in_str(c3,s1) ) x=0;
  1494.           if(vvv&32)printf(" x c123=%d %c %c %c\n",x-x0,c1,c2,c3);
  1495.         }
  1496.         if( 2*m3>y1-y0 )
  1497.         if(!x){
  1498.           boxa=*box2;boxb=*box2,boxc=*box2;    // copy contents
  1499.           boxa.next=&boxb;boxb.pre=&boxa;   // new pointers
  1500.           x2=i3; x=i2; if(x>x2){ k=x;x=x2;x2=k; }
  1501.           boxa.x=x0;  boxa.y=y0;boxa.x1=x;
  1502.           boxb.x=x+1; boxb.y=y0;boxb.x0=x+1;boxb.x1=x2;
  1503.           boxc.x=x2+1;boxc.y=y0;boxc.x0=x2+1;
  1504.           c1=whatletter(&boxa,cs); // unknown startpos!
  1505.           c2=whatletter(&boxb,cs);
  1506.           c3=whatletter(&boxc,cs);
  1507.           if( in_str(c1,s1)  || in_str(c2,s1)   || in_str(c3,s1) ) x=0;
  1508.           if(vvv&32)printf(" x c123=%d %c %c %c\n",x-x0,c1,c2,c3);
  1509.         }
  1510.       }
  1511.       if(x>x0 && x<x1){            // seperate first
  1512.         box2->y0=boxb.y0;
  1513.         box2->y1=boxb.y1;
  1514.         // --- insert ind list
  1515.         box3=new struct box((struct box)boxa);  // *box2=>boxa,boxb 024a4
  1516.         box3->x1=x;   box3->c=c1;
  1517.         box2->x0=x+1; box2->c=c2;
  1518.         box_ins_before(box2,box3); numC++;
  1519.         if(x2>x && x2<x1){
  1520.           // --- insert in list
  1521.           box3=new struct box((struct box)boxb);
  1522.           box3->x1=x2;   box3->c=c2;
  1523.           box2->x0=x2+1; box2->c=c3;
  1524.           box_ins_before(box2,box3); numC++;
  1525.         }
  1526.         continue; 
  1527.       }
  1528.   }
  1529.   if(vvv)printf(", numC %d\n",numC); 
  1530.  
  1531.  
  1532.   // ---- list output ---- for debugging
  1533.   if(vvv&6){
  1534.     for(i=0,box2=box1;box2;box2=box2->next,i++)
  1535.     if( strc(box2->c,lc) ){
  1536.       printf("# list chape %3d x=%4d %4d d=%3d %3d h=%d o=%d dots=%d %c\n",
  1537.         i,box2->x0,box2->y0,box2->x1-box2->x0+1,box2->y1-box2->y0+1,
  1538.         num_hole(box2->x0,box2->x1,box2->y0,box2->y1,p,cs),
  1539.         num_obj (box2->x0,box2->x1,box2->y0,box2->y1,p,cs),
  1540.         box2->dots,  box2->c );
  1541.       if( vvv&4 ){   
  1542. //        out_b(p,box2->x0,box2->y0,box2->x1-box2->x0+1,box2->y1-box2->y0+1,cs);
  1543.         out_x(box2);
  1544.       }
  1545.     } 
  1546.   }
  1547.  
  1548.   // ---- insert spaces ----
  1549.   for(i=0,box2=box1;box2;box2=box2->next,i++){
  1550.     char cc=0;
  1551.     if(box2->pre){
  1552.       if(box2->line!=box2->pre->line )
  1553.       if(lines.m3[box2->line] > lines.m4[box2->pre->line]) cc='\n'; // NL
  1554.       else cc=' ';
  1555.       if(box2->x0 > box2->pre->x1)
  1556.       if(box2->x0 - box2->pre->x1 > spc) cc=' ';     // SPC
  1557.       if(cc){
  1558. #if 0
  1559.         struct box *box3=new struct box ((struct box){
  1560.           box2->pre->x1+2,box2->x0-2,box2->y0,box2->y1,
  1561.           box2->x0-1,box2->y0,0,(struct box*)NULL,(struct box*)NULL,
  1562.           cc,0,box2->pre->line,0,0,0,0,&p});
  1563. #else
  1564.         struct box *box3=new struct box();
  1565.         box3->x0=box2->pre->x1+2;  box3->x1=box2->x0-2;
  1566.         box3->y0=box2->y0;         box3->y1=box2->y1;
  1567.         box3->x =box2->x0-1;       box3->y=box2->y0;
  1568.         box3->dots=0;              box3->c=cc;
  1569.         box3->next=box3->pre=NULL;
  1570.         box3->num=0; box3->line=box2->pre->line;
  1571.         box3->m1=0;  box3->m2=0;  box3->m3=0;  box3->m4=0;
  1572.         box3->p=&p;
  1573. #endif
  1574.         box_ins_before(box2,box3);
  1575.       }
  1576.     }
  1577.   }
  1578.  
  1579.   // ---- proof Il1 by context view ----
  1580.   // context: seperator, number, vokal, nonvokal, upper char ????
  1581.   if(vvv)printf("# step 4: context correction Il1 0O");
  1582.   if(!(mo&32))
  1583.   for(i=0,box2=box1->next;box2;box2=box2->next,i++){
  1584.     static char // *l_vokal="aeiou", *l_Vokal="AEIOU",
  1585.                 *l_nonvo="bcdfghjklmnpqrstvwxyz",
  1586.                 *l_small="abcdefghijklmnopqrstuvwxyz",
  1587.                 *l_digit="0123456789";
  1588.     if( in_str(box2->c,"Il1|") && box2->next && box2->pre ){ // tall chars
  1589. //       if( in_str(box2->pre->c," \n")      // SPC
  1590. //        && in_str(box2->next->c," \n") ) box2->c='I'; else // bad idea! I have ...
  1591.        if( in_str(box2->pre->c," \n")      // SPC
  1592.         && in_str(box2->next->c,l_nonvo) ) box2->c='I'; else
  1593.        if( in_str(box2->pre->c, l_small) ) box2->c='l'; else
  1594.        if( in_str(box2->pre->c, l_digit)  
  1595.         || in_str(box2->next->c,l_digit) ) box2->c='1';
  1596.     }
  1597.     if( in_str(box2->c,"O0") && box2->next && box2->pre ){
  1598.        if( in_str(box2->pre->c," \n")      // SPC
  1599.         && in_str(box2->next->c,l_nonvo) ) box2->c='O'; else
  1600.        if( in_str(box2->pre->c, l_digit)  
  1601.         || in_str(box2->next->c,l_digit) ) box2->c='0';
  1602.     }
  1603.     if(box2->pre->c==' ' && box2->c=='.'){
  1604.       box_del(box2->pre); // memory leak ???
  1605.     }
  1606.   }
  1607.   if(vvv)printf("\n");
  1608.  
  1609.   // ---- ASCII output
  1610.   for(i=0,box2=box1;box2;box2=box2->next,i++){
  1611.     printf("%c",box2->c);
  1612.     if(box2->c==' ')    // fill large gaps with spaces
  1613.     for(i=(box2->x1-box2->x0)/(2*env.avX+1);i>0;i--)printf(" ");
  1614.   }
  1615.   printf("\n");
  1616.  
  1617.   // ---- frame-size-histogramm
  1618.   // ---- (my own defined) distance between letters
  1619.   // ---- write internal picture of textsite
  1620.   // ----------- write out30.pgm ----------- how to mark mark tolerance ???
  1621.   if(vvv&32){
  1622.     for(box2=box1;box2;box2=box2->next){
  1623.       for(y=box2->y0;y<=box2->y1;y++)
  1624.       for(x=box2->x0;x<=box2->x1;x++){
  1625.        if( box2->c!=' ' && box2->c!='\n' )
  1626.        if( x==box2->x0 || x==box2->x1
  1627.         || y==box2->y0 || y==box2->y1 ) p2.p[x+y*p.x]|=32;  // box
  1628.        if( x> box2->x0 && x< box2->x1
  1629.         && y> box2->y0 && y< box2->y1 )
  1630.        if(box2->c=='_') p2.p[x+y*p.x]|=16;  // box
  1631.       }
  1632.     } 
  1633.     // writepgm("out30.pgm",p2);
  1634.     writebmp("out30.bmp",p2,vvv); // colored should be better
  1635.     // for(y=0;y<p.y;y++)for(x=0;x<p.x;x++)p2.p[x+p.x*y]=p.p[x+p.x*y];
  1636.   }
  1637.   
  1638.   delete p.p;
  1639.   delete p2.p;
  1640.   while(box1) { box2=box1->next; delete box1; box1=box2; } 
  1641. }
  1642.